﻿using System;
using System.IO;
using System.Net;
using System.Text;

namespace Storage.Core
{
	internal class BinarySerializer : IBinarySerializer
	{
		private BinarySerializer(Stream outStream)
		{
			this.outStream = outStream;
		}

		public static BinarySerializer BeginRecord(Stream outStream)
		{
			if (!outStream.CanWrite)
				throw new ArgumentException("Stream should be writeable", "outStream");
			return new BinarySerializer(outStream);
		}

		public int Position { get { return (int)outStream.Position; } }
		public bool CanSeek { get { return outStream.CanSeek; } }

		public IBinarySerializer Seek(int position)
		{
			outStream.Seek(position, SeekOrigin.Begin);
			return this;
		}

		public IBinarySerializer SeekToEnd()
		{
			outStream.Seek(0, SeekOrigin.End);
			return this;
		}

		public IBinarySerializer Write(bool b)
		{
			return Write((b ? 1 : 0));
		}

		public IBinarySerializer Write(byte b)
		{
			outStream.WriteByte(b);
			return this;
		}

		public IBinarySerializer Write(Int16 i)
		{
			return WriteToStream(BitConverter.GetBytes(i));
		}

		public IBinarySerializer Write(UInt16 i)
		{
			return WriteToStream(BitConverter.GetBytes(i));
		}

		public IBinarySerializer Write(Int32 i)
		{
			return WriteToStream(BitConverter.GetBytes(i));
		}

		public IBinarySerializer Write(UInt32 i)
		{
			return WriteToStream(BitConverter.GetBytes(i));
		}

		public IBinarySerializer Write(Int64 i)
		{
			return WriteToStream(BitConverter.GetBytes(i));
		}

		public IBinarySerializer Write(UInt64 i)
		{
			return WriteToStream(BitConverter.GetBytes(i));
		}

		public IBinarySerializer Write(float f)
		{
			return WriteToStream(BitConverter.GetBytes(f));
		}

		public IBinarySerializer Write(double d)
		{
			return WriteToStream(BitConverter.GetBytes(d));
		}

		public IBinarySerializer Write(Guid g)
		{
			return WriteToStream(g.ToByteArray());
		}

		public IBinarySerializer Write(byte[] bb)
		{
			return Write(bb, 0, bb.Length);
		}

		public IBinarySerializer Write(byte[] bb, int offset, int length)
		{
			Write(length);
			WriteToStream(bb, offset, length);
			return this;
		}

		public IBinarySerializer Write(string str)
		{
			return Write(str, Encoding.UTF8);
		}

		public IBinarySerializer Write(string str, Encoding encoding)
		{
			return Write(encoding.GetBytes(str));
		}

		public IBinarySerializer Write(IPAddress ip)
		{
			return Write(ip.GetAddressBytes());
		}

		public IBinarySerializer Write(IPEndPoint endPoint)
		{
			Write(endPoint.Address);
			return Write(endPoint.Port);
		}

		public IBinarySerializer Write(TimeSpan ts)
		{
			return Write(ts.Ticks);
		}

		public IBinarySerializer Write(DateTime dt)
		{
			return Write(dt.ToBinary());
		}

		private BinarySerializer WriteToStream(byte[] bytes)
		{
			return WriteToStream(bytes, 0, bytes.Length);
		}

		private BinarySerializer WriteToStream(byte[] bytes, int offset, int count)
		{
			outStream.Write(bytes, offset, count);
			return this;
		}

		private readonly Stream outStream;
	}
}